home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 7
/
Apprentice-Release7.iso
/
Source Code
/
Pascal
/
Applications
/
NIH Image 1.62b11
/
src
/
Projection.p
< prev
next >
Wrap
Text File
|
1996-03-01
|
44KB
|
989 lines
unit Projection;
{************************************************************************}
{* Projection.p *}
{* by Michael Castle (Pascal) and Janice Keller (assembly code) *}
{* University of Michigan Mental Health Research Institute (MHRI) *}
{* e-mail address: mike.castle@umich.edu *}
{* ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }
interface
uses
Types, Memory, QuickDraw, Packages, Menus, Events, Fonts, Scrap,
ToolUtils, Resources, Errors, Palettes, Dialogs, TextUtils,
Globals, Utilities, File2, File1, Graphics, Camera, Filters, Stacks;
procedure DoProjection;
function ShowProjectDialogBox: boolean;
procedure Project;
implementation
const
bigpowerof2 = 8192; {used for integer trigonometric arithmetic}
type
MHRIRealArray = array[1..MaxPics] of extended;
RealPoint = record
x: extended;
y: extended;
end; {record}
IntPtr = ^integer;
LongPtr = ^LongInt;
var
SliceInterval: extended; {distance, in pixels, between original slices}
BoundRect: rect; {boundary rectangle for a rectangular selection}
cancelled: boolean;
ProjSize: LongInt;
{******************************************************************************}
{* This procedure frees memory allocated for buffers used in projection calculations. *}
{******************************************************************************}
procedure DisposeProjectionPtrs (projptr, opaptr, brightcueptr: ptr; zbufptr, countbufptr, cuezbufptr: IntPtr; sumbufptr: LongPtr);
begin
if zbufptr <> nil then
DisposePtr(Ptr(zbufptr));
if opaptr <> nil then
DisposePtr(opaptr);
if sumbufptr <> nil then
DisposePtr(Ptr(sumbufptr));
if countbufptr <> nil then
DisposePtr(Ptr(countbufptr));
if cuezbufptr <> nil then
DisposePtr(Ptr(cuezbufptr));
if brightcueptr <> nil then
DisposePtr(brightcueptr);
if projptr <> nil then
DisposePtr(projptr);
end;
{******************************************************************************}
{* This procedure projects each pixel of a volume (stack of slices) onto a plane as the volume rotates about *}
{* the x-axis. Integer arithmetic, precomputation of values, and iterative addition rather than multiplication *}
{* inside a loop are used extensively to make the code run efficiently. Projection parameters stored in global *}
{* variables determine how the projection will be performed. This procedure returns various buffers which *}
{* are actually used by DoProject to find the final projected image for the volume of slices at the current angle.*}
{******************************************************************************}
procedure DoOneProjectionX (nSlices, ycenter, zcenter: integer; projwidth, projheight: LongInt; costheta, sintheta: longint; projptr, opaptr, brightcueptr: ptr; zbufptr, cuezbufptr, countbufptr: IntPtr; sumbufptr: LongPtr; str: string);
var
i, j, k, {loop indices}
thispixel: integer; {the current pixel to be projected}
offset, offsetinit: longint; {precomputed offsets into an image buffer}
projaddr, {buffer address for final projected image}
opaaddr, {buffer address for opacity surface projection component}
brightcueaddr, {buffer address for brightest-point projection with interior depth cues}
zbufaddr, {z-buffer address for surface projections (nearest-point/opacity)}
cuezbufaddr, {z-buffer address for brightest-point projection with interior depth cues}
countbufaddr, {buffer addr for counting pixels in mean-value projection}
sumbufaddr: longint; {buffer addr for summing pixels in mean-value projection}
z, {z-coordinate of points in current slice before rotation}
ynew, znew, {y- and z-coordinates of current point after rotation}
zmax, zmin, {z-coordinates of first and last slices before rotation}
zmaxminuszmintimes100: longint; {precomputed values to save time in loops}
c100minusDepthCueInt, c100minusDepthCueSurf: integer;
DepthCueIntlessthan100, DepthCueSurflessthan100: boolean;
OpacityOrNearestPt, OpacityAndNotNearestPt: boolean;
MeanVal, BrightestPt: boolean;
ysintheta, ycostheta, {values used in rotational calculations before projection}
zsintheta, zcostheta, ysinthetainit, ycosthetainit: longint;
p, {pointer to final projected image buffer}
op, {pointer to opacity surface projection buffer}
bp: ptr; {pointer to brightest-point projection buffer with interior depth cues}
zp, {pointer to surface projection (nearest-point/opacity) z-buffer}
qp, {pointer to z-buffer for brightest-point projection with interior depth cues}
cp: IntPtr; {pointer to buffer for counting pixels for mean-value projection}
sp: LongPtr; {pointer to mean-value summing buffer}
width: integer;
theLine: LineType;
begin
with BoundRect do begin {precompute values to avoid computations in loop}
width := right - left;
zmax := zcenter + projheight div 2; {find z-coordinates of first and last slices}
zmin := zcenter - projheight div 2;
zmaxminuszmintimes100 := 100 * (zmax - zmin);
c100minusDepthCueInt := 100 - DepthCueInt;
c100minusDepthCueSurf := 100 - DepthCueSurf;
DepthCueIntlessthan100 := DepthCueInt < 100;
DepthCueSurflessthan100 := DepthCueSurf < 100;
OpacityOrNearestPt := (ProjectionMethod = NearestPoint) or (Opacity > 0);
OpacityAndNotNearestPt := (Opacity > 0) and (ProjectionMethod <> NearestPoint);
MeanVal := ProjectionMethod = MeanValue;
BrightestPt := ProjectionMethod = BrightestPoint;
projaddr := ord4(projptr);
opaaddr := ord4(opaptr);
brightcueaddr := ord4(brightcueptr);
zbufaddr := ord4(zbufptr);
cuezbufaddr := ord4(cuezbufptr);
countbufaddr := ord4(countbufptr);
sumbufaddr := ord4(sumbufptr);
ycosthetainit := (top - ycenter - 1) * costheta;
ysinthetainit := (top - ycenter - 1) * sintheta;
offsetinit := ((projheight - bottom + top) div 2) * projwidth + (projwidth - right + left) div 2 - 1;
for k := 1 to nSlices do begin
SelectSlice(k);
z := round((k - 1) * SliceInterval) - zcenter;
zcostheta := z * costheta;
zsintheta := z * sintheta;
ycostheta := ycosthetainit;
ysintheta := ysinthetainit;
for j := top to bottom - 1 do begin {read each row in the current slice}
ycostheta := ycostheta + costheta; {rotate about x-axis and find new y,z}
ysintheta := ysintheta + sintheta; {x-coordinates will not change}
ynew := (ycostheta - zsintheta) div bigpowerof2 + ycenter - top;
znew := (ysintheta + zcostheta) div bigpowerof2 + zcenter;
offset := offsetinit + ynew * projwidth;
GetLine(left, j, width, theLine);
for i := 0 to width - 1 do begin {read each pixel in current row and project it}
thispixel := theLine[i];
offset := offset + 1;
if (offset >= ProjSize) or (offset < 0) then
offset := 0;
if (thispixel <= TransparencyUpper) and (thispixel >= TransparencyLower) then begin
if OpacityOrNearestPt then begin
zp := IntPtr(zbufaddr + offset + offset);
if (znew < zp^) then begin
zp^ := znew;
if OpacityAndNotNearestPt then begin
op := ptr(opaaddr + offset);
if (DepthCueSurflessthan100) then
op^ := 255 - (DepthCueSurf * (255 - thispixel) div 100 + (c100minusDepthCueSurf) * (255 - thispixel) * (zmax - znew) div zmaxminuszmintimes100)
else
op^ := thispixel;
end
else begin
p := ptr(projaddr + offset);
if DepthCueSurflessthan100 then
p^ := 255 - (DepthCueSurf * (255 - thispixel) div 100 + (c100minusDepthCueSurf) * (255 - thispixel) * (zmax - znew) div zmaxminuszmintimes100)
else
p^ := thispixel;
end;
end;
end; {NearestPoint case}
if MeanVal then begin
sp := LongPtr(sumbufaddr + offset + offset + offset + offset);
sp^ := sp^ + thispixel;
cp := IntPtr(countbufaddr + offset + offset);
cp^ := cp^ + 1;
end {MeanValue case}
else if BrightestPt then begin
if (DepthCueIntlessthan100) then begin
p := ptr(projaddr + offset);
bp := ptr(brightcueaddr + offset);
qp := IntPtr(cuezbufaddr + offset + offset);
if (thispixel < BAND(bp^, 255)) or ((thispixel = BAND(bp^, 255)) and (znew < qp^)) then begin
bp^ := thispixel; {use z-buffer to ensure that if depth-cueing is on, }
qp^ := znew; {the closer of two equally-bright points is displayed}
p^ := 255 - (DepthCueInt * (255 - thispixel) div 100 + (c100minusDepthCueInt) * (255 - thispixel) * (zmax - znew) div zmaxminuszmintimes100);
end; {then}
end
else begin
p := ptr(projaddr + offset);
if (thispixel < BAND(p^, 255)) then
p^ := thispixel;
end; {else}
end; {BrightestPoint case}
end;
end; {for j}
end; {for i}
UpdateMeter(10 + (k * 90) div nSlices, str);
if CommandPeriod then begin
cancelled := true;
beep;
leave;
end;
end; {for k}
end; {with}
end; {procedure DoOneProjectionX}
{******************************************************************************}
{* This procedure projects each pixel of a volume (stack of slices) onto a plane as the volume rotates about *}
{* the y-axis. Integer arithmetic, precomputation of values, and iterative addition rather than multiplication *}
{* inside a loop are used extensively to make the code run efficiently. Projection parameters stored in global *}
{* variables determine how the projection will be performed. This procedure returns various buffers which *}
{* are actually used by DoProject to find the final projected image for the volume of slices at the current angle.*}
{******************************************************************************}
procedure DoOneProjectionY (nSlices, xcenter, zcenter: integer; projwidth, projheight: LongInt; costheta, sintheta: longint; projptr, opaptr, brightcueptr: ptr; zbufptr, cuezbufptr, countbufptr: IntPtr; sumbufptr: LongPtr; str: string);
var
i, j, k, thispixel: integer;
offset, offsetinit: longint;
projaddr, opaaddr, brightcueaddr, zbufaddr, cuezbufaddr, countbufaddr, sumbufaddr: longint;
z, xnew, znew, zmax, zmin, zmaxminuszmintimes100: longint;
c100minusDepthCueInt, c100minusDepthCueSurf: integer;
DepthCueIntlessthan100, DepthCueSurflessthan100: boolean;
OpacityOrNearestPt, OpacityAndNotNearestPt: boolean;
MeanVal, BrightestPt: boolean;
xsintheta, xcostheta, zsintheta, zcostheta, xsinthetainit, xcosthetainit: longint;
p, op, bp: ptr;
zp, qp, cp: IntPtr;
sp: LongPtr;
width: integer;
aLine: LineType;
begin
with BoundRect do begin
width := right - left;
zmax := zcenter + projwidth div 2;
zmin := zcenter - projwidth div 2;
zmaxminuszmintimes100 := 100 * (zmax - zmin);
c100minusDepthCueInt := 100 - DepthCueInt;
c100minusDepthCueSurf := 100 - DepthCueSurf;
DepthCueIntlessthan100 := DepthCueInt < 100;
DepthCueSurflessthan100 := DepthCueSurf < 100;
OpacityOrNearestPt := (ProjectionMethod = NearestPoint) or (Opacity > 0);
OpacityAndNotNearestPt := (Opacity > 0) and (ProjectionMethod <> NearestPoint);
MeanVal := ProjectionMethod = MeanValue;
BrightestPt := ProjectionMethod = BrightestPoint;
projaddr := ord4(projptr);
opaaddr := ord4(opaptr);
brightcueaddr := ord4(brightcueptr);
zbufaddr := ord4(zbufptr);
cuezbufaddr := ord4(cuezbufptr);
countbufaddr := ord4(countbufptr);
sumbufaddr := ord4(sumbufptr);
xcosthetainit := (left - xcenter - 1) * costheta;
xsinthetainit := (left - xcenter - 1) * sintheta;
for k := 1 to nSlices do begin
SelectSlice(k);
z := round((k - 1) * SliceInterval) - zcenter;
zcostheta := z * costheta;
zsintheta := z * sintheta;
offsetinit := ((projheight - bottom + top) div 2) * projwidth + (projwidth - right + left) div 2 - projwidth;
for j := top to bottom - 1 do begin
xcostheta := xcosthetainit;
xsintheta := xsinthetainit;
offsetinit := offsetinit + projwidth;
GetLine(left, j, width, aLine);
for i := 0 to width - 1 do begin
thispixel := aLine[i];
xcostheta := xcostheta + costheta;
xsintheta := xsintheta + sintheta;
if (thispixel <= TransparencyUpper) and (thispixel >= TransparencyLower) then begin
xnew := (xcostheta + zsintheta) div bigpowerof2 + xcenter - left;
znew := (zcostheta - xsintheta) div bigpowerof2 + zcenter;
offset := offsetinit + xnew;
if (offset >= ProjSize) or (offset < 0) then
offset := 0;
if OpacityOrNearestPt then begin
zp := IntPtr(zbufaddr + offset + offset);
if (znew < zp^) then begin
zp^ := znew;
if OpacityAndNotNearestPt then begin
op := ptr(opaaddr + offset);
if (DepthCueSurflessthan100) then
op^ := 255 - (DepthCueSurf * (255 - thispixel) div 100 + (c100minusDepthCueSurf) * (255 - thispixel) * (zmax - znew) div zmaxminuszmintimes100)
else
op^ := thispixel;
end
else begin
p := ptr(projaddr + offset);
if DepthCueSurflessthan100 then
p^ := 255 - (DepthCueSurf * (255 - thispixel) div 100 + (c100minusDepthCueSurf) * (255 - thispixel) * (zmax - znew) div zmaxminuszmintimes100)
else
p^ := thispixel;
end;
end;
end; {NearestPoint case}
if MeanVal then begin
sp := LongPtr(sumbufaddr + offset + offset + offset + offset);
sp^ := sp^ + thispixel;
cp := IntPtr(countbufaddr + offset + offset);
cp^ := cp^ + 1;
end {MeanValue case}
else if BrightestPt then begin
if (DepthCueIntlessthan100) then begin
p := ptr(projaddr + offset);
bp := ptr(brightcueaddr + offset);
qp := IntPtr(cuezbufaddr + offset + offset);
if (thispixel < BAND(bp^, 255)) or ((thispixel = BAND(bp^, 255)) and (znew < qp^)) then begin
bp^ := thispixel;
qp^ := znew;
p^ := 255 - (DepthCueInt * (255 - thispixel) div 100 + (c100minusDepthCueInt) * (255 - thispixel) * (zmax - znew) div zmaxminuszmintimes100);
end; {then}
end
else begin
p := ptr(projaddr + offset);
if (thispixel < BAND(p^, 255)) then
p^ := thispixel;
end; {else}
end; {BrightestPoint case}
end;
end; {for j}
end; {for i}
UpdateMeter(10 + (k * 90) div nSlices, str);
if CommandPeriod then begin
cancelled := true;
beep;
leave;
end;
end; {for k}
end; {with}
end; {procedure DoOneProjectionY}
{******************************************************************************}
{* This procedure projects each pixel of a volume (stack of slices) onto a plane as the volume rotates about *}
{* the z-axis. Integer arithmetic, precomputation of values, and iterative addition rather than multiplication *}
{* inside a loop are used extensively to make the code run efficiently. Projection parameters stored in global *}
{* variables determine how the projection will be performed. This procedure returns various buffers which *}
{* are actually used by DoProject to find the final projected image for the volume of slices at the current angle.*}
{******************************************************************************}
procedure DoOneProjectionZ (nSlices, xcenter, ycenter, zcenter: integer; projwidth, projheight: LongInt; costheta, sintheta: longint; projptr, opaptr, brightcueptr: ptr; zbufptr, cuezbufptr, countbufptr: IntPtr; sumbufptr: LongPtr; str: string);
var
i, j, k, thispixel: integer;
offset, offsetinit: longint;
projaddr, opaaddr, brightcueaddr, zbufaddr, cuezbufaddr, countbufaddr, sumbufaddr: longint;
z, xnew, ynew, zmax, zmin, zmaxminuszmintimes100: longint;
c100minusDepthCueInt, c100minusDepthCueSurf: integer;
DepthCueIntlessthan100, DepthCueSurflessthan100: boolean;
OpacityOrNearestPt, OpacityAndNotNearestPt: boolean;
MeanVal, BrightestPt: boolean;
xsintheta, xcostheta, ysintheta, ycostheta: longint;
xsinthetainit, xcosthetainit, ysinthetainit, ycosthetainit: longint;
p, op, bp: ptr;
zp, qp, cp: IntPtr;
sp: LongPtr;
width: integer;
theLine: LineType;
begin
with BoundRect do begin
width := right - left;
zmax := zcenter + projwidth div 2;
zmin := zcenter - projwidth div 2;
zmaxminuszmintimes100 := 100 * (zmax - zmin);
c100minusDepthCueInt := 100 - DepthCueInt;
c100minusDepthCueSurf := 100 - DepthCueSurf;
DepthCueIntlessthan100 := DepthCueInt < 100;
DepthCueSurflessthan100 := DepthCueSurf < 100;
OpacityOrNearestPt := (ProjectionMethod = NearestPoint) or (Opacity > 0);
OpacityAndNotNearestPt := (Opacity > 0) and (ProjectionMethod <> NearestPoint);
MeanVal := ProjectionMethod = MeanValue;
BrightestPt := ProjectionMethod = BrightestPoint;
projaddr := ord4(projptr);
opaaddr := ord4(opaptr);
brightcueaddr := ord4(brightcueptr);
zbufaddr := ord4(zbufptr);
cuezbufaddr := ord4(cuezbufptr);
countbufaddr := ord4(countbufptr);
sumbufaddr := ord4(sumbufptr);
xcosthetainit := (left - xcenter - 1) * costheta;
xsinthetainit := (left - xcenter - 1) * sintheta;
ycosthetainit := (top - ycenter - 1) * costheta;
ysinthetainit := (top - ycenter - 1) * sintheta;
offsetinit := ((projheight - bottom + top) div 2) * projwidth + (projwidth - right + left) div 2 + left - 1;
for k := 1 to nSlices do begin
SelectSlice(k);
z := round((k - 1) * SliceInterval) - zcenter;
ycostheta := ycosthetainit;
ysintheta := ysinthetainit;
for j := top to bottom - 1 do begin
ycostheta := ycostheta + costheta;
ysintheta := ysintheta + sintheta;
xcostheta := xcosthetainit;
xsintheta := xsinthetainit;
GetLine(left, j, width, theLine);
for i := 0 to width - 1 do begin
thisPixel := theLine[i];
xcostheta := xcostheta + costheta;
xsintheta := xsintheta + sintheta;
if (thispixel <= TransparencyUpper) and (thispixel >= TransparencyLower) then begin
xnew := (xcostheta - ysintheta) div bigpowerof2 + xcenter - left;
ynew := (xsintheta + ycostheta) div bigpowerof2 + ycenter - top;
offset := offsetinit + ynew * projwidth + xnew;
if (offset >= ProjSize) or (offset < 0) then
offset := 0;
if OpacityOrNearestPt then begin
zp := IntPtr(zbufaddr + offset + offset);
if (z < zp^) then begin
zp^ := z;
if OpacityAndNotNearestPt then begin
op := ptr(opaaddr + offset);
if (DepthCueSurflessthan100) then
op^ := 255 - (DepthCueSurf * (255 - thispixel) div 100 + (c100minusDepthCueSurf) * (255 - thispixel) * (zmax - z) div zmaxminuszmintimes100)
else
op^ := thispixel;
end
else begin
p := ptr(projaddr + offset);
if DepthCueSurflessthan100 then
p^ := 255 - (DepthCueSurf * (255 - thispixel) div 100 + (c100minusDepthCueSurf) * (255 - thispixel) * (zmax - z) div zmaxminuszmintimes100)
else
p^ := thispixel;
end;
end;
end; {NearestPoint case}
if MeanVal then begin
sp := LongPtr(sumbufaddr + offset + offset + offset + offset);
sp^ := sp^ + thispixel;
cp := IntPtr(countbufaddr + offset + offset);
cp^ := cp^ + 1;
end {MeanValue case}
else if BrightestPt then begin
if (DepthCueIntlessthan100) then begin
p := ptr(projaddr + offset);
bp := ptr(brightcueaddr + offset);
qp := IntPtr(cuezbufaddr + offset + offset);
if (thispixel < BAND(bp^, 255)) or ((thispixel = BAND(bp^, 255)) and (z < qp^)) then begin
bp^ := thispixel;
qp^ := z;
p^ := 255 - (DepthCueInt * (255 - thispixel) div 100 + (c100minusDepthCueInt) * (255 - thispixel) * (zmax - z) div zmaxminuszmintimes100);
end; {then}
end
else begin
p := ptr(projaddr + offset);
if (thispixel < BAND(p^, 255)) then
p^ := thispixel;
end; {else}
end; {BrightestPoint case}
end;
end; {for j}
end; {for i}
UpdateMeter(10 + (k * 90) div nSlices, str);
if CommandPeriod then begin
cancelled := true;
beep;
leave;
end;
end; {for k}
end; {with}
end; {procedure DoOneProjectionZ}
{******************************************************************************}
{* This code initializes buffers by filling them with uniform values. The *}
{* The buffer can be filled with one, two or four byte values. *}
{******************************************************************************}
procedure InitializeBuffer (p: ptr; size: longint; value, step: integer);
type
IntPtrType=^integer;
LongPtrType=^LongInt;
var
i, Lstep: longint;
IntPtr:IntPtrTYpe;
LongPtr:LongPtrType;
begin
Lstep:=step;
case step of
1: for i := 1 to size do begin
p^ := value;
p := Ptr(ord4(p) + Lstep);
end;
2: begin
IntPtr:=IntPtrType(p);
for i := 1 to size do begin
IntPtr^ := value;
IntPtr := IntPtrType(ord4(IntPtr) + Lstep);
end;
end;
4: begin
LongPtr:=LongPtrType(p);
for i := 1 to size do begin
LongPtr^ := value;
LongPtr := LongPtrType(ord4(LongPtr) + Lstep);
end;
end;
end;
end;
{******************************************************************************}
{* This procedure creates a sequence of projections of a rotating volume (stack of slices) onto a plane using *}
{* nearest-point (surface), brightest-point, or mean-value projection or a weighted combination of nearest- *}
{* point projection with either of the other two methods (partial opacity). The user may choose to rotate the *}
{* volume about any of the three orthogonal axes (x,y, or z), make portions of the volume transparent, or add *}
{* a greater degree of visual realism by employing depth cues. *}
{******************************************************************************}
procedure DoProjection;
var
tport: Grafptr;
nSlices: integer; {number of slices in volume}
projwidth, projheight: LongInt; {dimensions of projection image}
xcenter, ycenter, zcenter: integer; {coordinates of center of volume of rotation}
theta: integer; {current angle of rotation in degrees}
thetarad: extended; {current angle of rotation in radians}
sintheta, costheta: longint; {sine and cosine of current angle}
xsinthetainit, xcosthetainit: longint; {precomputed products to avoid mult in loops}
offset, MemoryRequired: longint;
p, op, bp, projptr, opaptr, brightcueptr: ptr;
zp, zbufptr, qp, cuezbufptr, countbufptr, cp: IntPtr;
sp, sumbufptr: LongPtr;
curval, prevval, nextval, aboveval, belowval: integer;
ignore: integer; {irrelevant return value from a function}
ticksinit, tickstogo, TicksForOneProjection: longint;
str, TimeStr, seconds: str255;
SaveProjectionsTemp, AutoSelectAll, AllocatingBuffers: boolean;
n, nProjections, angle: integer;
SourceInfo, DestInfo: InfoPtr;
width, height: LongInt;
procedure Abort;
begin
DisposeProjectionPtrs(projptr, opaptr, brightcueptr, zbufptr, countbufptr, cuezbufptr, sumbufptr);
if AllocatingBuffers and (MaxBlock > 20000) then
PutError('Insufficient Memory.');
AbortMacro;
{exit(DoProjection);} {ppc-bug}
end;
begin
ShowWatch;
AutoSelectAll := not Info^.RoiShowing;
if AutoSelectAll then
SelectAll(false);
if NotInBounds then
exit(DoProjection);
cancelled := false;
SourceInfo := Info;
GetPort(tPort);
with Info^ do begin
SetPort(GrafPtr(osPort));
BoundRect := Roirect;
end;
if (AngleInc = 0) and (TotalAngle <> 0) then
AngleInc := 5;
angle := 0;
nProjections := 0;
if AngleInc = 0 then
nProjections := 1
else
while angle <= TotalAngle do begin
nProjections := nProjections + 1;
angle := angle + AngleInc;
end;
if angle > 360 then
nProjections := nProjections - 1;
if nProjections <= 0 then
nProjections := 1;
nSlices := Info^.StackInfo^.nSlices; {get number of slices in volume}
SliceInterval := info^.StackInfo^.SliceSpacing;
with BoundRect do begin
width := right - left;
height := bottom - top;
xcenter := (left + right) div 2; {find center of volume of rotation}
ycenter := (top + bottom) div 2;
zcenter := round(nSlices * SliceInterval / 2.0);
if MinProjSize and (AxisOfRotation <> ZAxis) then begin
case AxisOfRotation of {find dimensions of projection image}
XAxis: begin
projheight := round(sqrt(sqr(nSlices * SliceInterval) + height * height));
projwidth := right - left;
end; {XAxis}
YAxis: begin
projwidth := round(sqrt(sqr(nSlices * SliceInterval) + ord4(right - left) * (right - left)));
projheight := bottom - top;
end; {YAxis}
end; {case}
end {then}
else begin
projwidth := round(sqrt(sqr(nSlices * SliceInterval) + width * width));
projheight := round(sqrt(sqr(nSlices * SliceInterval) + height * height));
end; {else make all windows the same size regardless of rotation axis}
end; {with BoundRect}
if odd(projwidth) then
projwidth := projwidth + 1;
projptr := nil;
zbufptr := nil;
opaptr := nil;
brightcueptr := nil;
cuezbufptr := nil;
sumbufptr := nil;
countbufptr := nil;
AllocatingBuffers := true;
projsize := projwidth * projheight;
projptr := NewPtr(projsize);
if projptr = nil then
begin Abort; exit(DoProjection) end;
if (ProjectionMethod = NearestPoint) or (Opacity > 0) then begin {get other required buffers}
zbufptr := IntPtr(NewPtr(projsize * 2));
if zbufptr = nil then
begin Abort; exit(DoProjection) end;
end;
if (Opacity > 0) and (ProjectionMethod <> NearestPoint) then begin
opaptr := NewPtr(projsize);
if opaptr = nil then
begin Abort; exit(DoProjection) end;
end;
if (ProjectionMethod = BrightestPoint) and (DepthCueInt < 100) then begin
brightcueptr := NewPtr(projsize);
cuezbufptr := IntPtr(NewPtr(projsize * 2));
if (brightcueptr = nil) or (cuezbufptr = nil) then
begin Abort; exit(DoProjection) end;
end;
if (ProjectionMethod = MeanValue) then begin
sumbufptr := LongPtr(NewPtr(projsize * 4));
countbufptr := IntPtr(NewPtr(projsize * 2));
if (sumbufptr = nil) or (countbufptr = nil) then
begin Abort; exit(DoProjection) end;
end;
AllocatingBuffers := false;
SaveProjectionsTemp := FALSE; {check whether we have enough memory to open}
MemoryRequired := nProjections * projsize + SizeOf(PicInfo) + MinFree;
if (MemoryRequired > FreeMem) and not (SaveProjections) then begin
str := 'Insufficient memory to create entire stack of projections. Projection images will be saved to disk.';
if (PutMessageWithCancel(str) = cancel) then
begin Abort; exit(DoProjection) end;
SaveProjections := TRUE;
SaveProjectionsTemp := TRUE;
end;
if (SaveProjections) then begin {prepare to save projections as created if desired}
SaveAsWhat := AsTiff;
SaveAllState := SaveAllStage1;
end;
TimeStr := '?';
theta := InitAngle; {begin rotation with user-specified angle}
ticksinit := TickCount;
for n := 1 to nProjections do begin
if (SaveProjections) or (n = 1) then begin {open new window or stack slice}
if SaveProjections then
case AxisOfRotation of
XAxis:
str := StringOf('Projection X ', theta:3);
YAxis:
str := StringOf('Projection Y ', theta:3);
ZAxis:
str := StringOf('Projection Z ', theta:3);
end
else
str := 'Projections';
if not NewPicWindow(str, projwidth, projheight) then
begin Abort; exit(DoProjection) end;
end
else if not (AddSlice(false)) then
begin Abort; exit(DoProjection) end;
str := concat('Projecting: ', long2str(n), '/', long2str(nProjections), ' (', long2str(Theta), '°', ', ', TimeStr, ')');
ShowMeter;
UpdateMeter(0, str);
thetarad := theta * pi / 180.0;
costheta := round(bigpowerof2 * cos(thetarad));
sintheta := round(bigpowerof2 * sin(thetarad));
p := projptr; {initialize required projection buffers}
InitializeBuffer(p, projsize, 255, 1);
if (ProjectionMethod = NearestPoint) or (Opacity > 0) then begin
zp := zbufptr;
InitializeBuffer(Ptr(zp), projsize, 32767, 2);
end; {then}
if (Opacity > 0) and (ProjectionMethod <> NearestPoint) then begin
op := opaptr;
InitializeBuffer(op, projsize, 255, 1);
end; {then}
if (ProjectionMethod = MeanValue) then begin
sp := sumbufptr;
cp := countbufptr;
InitializeBuffer(Ptr(sp), projsize, 0, 4);
InitializeBuffer(Ptr(cp), projsize, 0, 2);
end;
if (ProjectionMethod = BrightestPoint) and (DepthCueInt < 100) then begin
bp := brightcueptr;
InitializeBuffer(bp, projsize, 255, 1);
qp := cuezbufptr;
InitializeBuffer(Ptr(qp), projsize, 255, 2);
end; {then}
UpdateMeter(10, str);
DestInfo := Info;
Info := SourceInfo;
case AxisOfRotation of
XAxis:
DoOneProjectionX(nSlices, ycenter, zcenter, projwidth, projheight, costheta, sintheta, projptr, opaptr, brightcueptr, zbufptr, cuezbufptr, countbufptr, sumbufptr, str);
YAxis:
DoOneProjectionY(nSlices, xcenter, zcenter, projwidth, projheight, costheta, sintheta, projptr, opaptr, brightcueptr, zbufptr, cuezbufptr, countbufptr, sumbufptr, str);
ZAxis:
DoOneProjectionZ(nSlices, xcenter, ycenter, zcenter, projwidth, projheight, costheta, sintheta, projptr, opaptr, brightcueptr, zbufptr, cuezbufptr, countbufptr, sumbufptr, str);
end;
Info := DestInfo;
if n = 1 then
TicksForOneProjection := TickCount - TicksInit;
TicksToGo := (nProjections - n) * TicksForOneProjection;
NumToString((TicksToGo div 60) mod 60, seconds);
if length(seconds) = 1 then
seconds := concat('0', seconds);
timestr := concat(long2str(TicksToGo div 3600), ':', seconds);
if (ProjectionMethod = MeanValue) then begin
p := projptr; {calculate the mean-value image from returned info}
sp := sumbufptr;
cp := countbufptr;
for offset := 0 to projsize - 1 do begin
if (cp^ > 0) then
p^ := sp^ div cp^;
p := ptr(ord4(p) + 1);
sp := LongPtr(ord4(sp) + 4);
cp := IntPtr(ord4(cp) + 2);
end; {for}
end; {then}
if (Opacity > 0) and (ProjectionMethod <> NearestPoint) then begin
p := projptr; {calculate surface proj component (opacity on)}
op := opaptr; {and combine with another proj component}
for offset := 0 to projsize - 1 do begin
p^ := (Opacity * BAND(op^, 255) + (100 - Opacity) * BAND(p^, 255)) div 100;
p := ptr(ord4(p) + 1);
op := ptr(ord4(op) + 1);
end; {for}
end; {then}
if (AxisOfRotation = ZAxis) then begin {interpolate for z-axis rotation}
p := ptr(ord4(projptr) + projwidth);
for offset := projwidth to projsize - 1 - projwidth do begin
curval := BAND(p^, 255);
prevval := BAND(ptr(ord4(p) - 1)^, 255);
nextval := BAND(ptr(ord4(p) + 1)^, 255);
aboveval := BAND(ptr(ord4(p) - projwidth)^, 255);
belowval := BAND(ptr(ord4(p) + projwidth)^, 255);
if (curval = 255) and (prevval <> 255) and (nextval <> 255) and (aboveval <> 255) and (belowval <> 255) then
p^ := (prevval + nextval + aboveval + belowval) div 4;
p := ptr(ord4(p) + 1);
end;
end;
if (SaveProjections) or (n = 1) then
BlockMove(projptr, Info^.PicBaseAddr, projsize) {whole ROI write to projection image}
else
BlockMove(projptr, Info^.StackInfo^.PicBaseH[n]^, projsize);
UpdateMeter(-1, ''); {dispose of meter}
if cancelled then begin
if n > 1 then
DeleteSlice;
leave;
end;
if (SaveProjections) then begin
SaveAs('', 0); {just save and put away current image after creating it}
ignore := CloseAWindow(info^.wptr);
end
else if n = 1 then begin {create new stack for first projection if not saving projections}
if not MakeStackFromWindow then
begin Abort; exit(DoProjection) end
end;
theta := (theta + AngleInc) mod 360;
UpdatePicWindow;
end; {for}
SaveAllState := NoSaveAll;
if SaveProjectionsTemp then {turn this back off if we turned it on due to lack of memory}
SaveProjections := FALSE;
DisposeProjectionPtrs(projptr, opaptr, brightcueptr, zbufptr, countbufptr, cuezbufptr, sumbufptr);
SetPort(tPort);
DestInfo := info;
info := SourceInfo;
SelectSlice(info^.StackInfo^.CurrentSlice);
if AutoSelectAll then
KillRoi;
info := DestInfo;
end; {procedure DoProjection}
{******************************************************************************}
{* This procedure allows the user to set parameters for projection in a large dialog box. *}
{******************************************************************************}
function ShowProjectDialogBox: boolean;
const
ProjectOptionsID = 128;
SliceIntervalID = 3;
InitAngleID = 4;
TotalAngleID = 5;
AngleIncID = 6;
TransparencyLowerID = 7;
TransparencyUpperID = 8;
OpacityID = 9;
DepthCueSurfID = 10;
DepthCueIntID = 11;
RotationAboutXID = 12;
RotationAboutYID = 13;
RotationAboutZID = 14;
SaveProjectionsID = 15;
MinProjSizeID = 16;
NearestID = 28;
BrightestID = 29;
MeanID = 30;
var
mylog: Dialogptr; {pointer to dialog box}
i, item, alldone: integer;
SaveInitAngle, SaveTotalAngle, SaveAngleInc: integer;
SaveOpacity: integer;
SaveAxisOfRotation: AxisType;
SaveSaveProjections, SaveCloseSlices, SaveMinProjSize: Boolean;
SaveLower, SaveUpper: integer;
PercentSurf, PercentInt: integer;
interval: extended;
begin
InitCursor;
Interval := info^.StackInfo^.SliceSpacing;
if Interval <= 0.0 then
Interval := 1.0;
SaveInitAngle := InitAngle;
SaveTotalAngle := TotalAngle;
SaveAngleInc := AngleInc;
SaveOpacity := Opacity;
SaveAxisOfRotation := AxisOfRotation;
SaveSaveProjections := SaveProjections;
SaveMinProjSize := MinProjSize;
SaveLower := TransparencyLower;
SaveUpper := TransparencyUpper;
PercentSurf := 100 - DepthCueSurf;
PercentInt := 100 - DepthCueInt;
if DensitySlicing then
with info^ do begin
TransparencyLower := SliceStart;
TransparencyUpper := SliceEnd;
end;
mylog := GetNewDialog(ProjectOptionsID, nil, pointer(-1));
SetDReal(MyLog, SliceIntervalID, Interval, 1);
SelectdialogItemText(MyLog, SliceIntervalID, 0, 32767);
SetDNum(MyLog, InitAngleID, InitAngle);
SetDNum(MyLog, TotalAngleID, TotalAngle);
SetDNum(MyLog, AngleIncID, AngleInc);
SetDNum(MyLog, TransparencyLowerID, TransparencyLower);
SetDNum(MyLog, TransparencyUpperID, TransparencyUpper);
SetDNum(MyLog, OpacityID, Opacity);
SetDNum(MyLog, DepthCueSurfID, PercentSurf);
SetDNum(MyLog, DepthCueIntID, PercentInt);
OutlineButton(MyLog, ok, 16);
SetDlogItem(MyLog, RotationAboutXID, ord(AxisOfRotation = XAxis));
SetDlogItem(MyLog, RotationAboutYID, ord(AxisOfRotation = YAxis));
SetDlogItem(MyLog, RotationAboutZID, ord(AxisOfRotation = ZAxis));
SetDlogItem(MyLog, NearestID, ord(ProjectionMethod = NearestPoint));
SetDlogItem(MyLog, BrightestID, ord(ProjectionMethod = BrightestPoint));
SetDlogItem(MyLog, MeanID, ord(ProjectionMethod = MeanValue));
SetDlogItem(MyLog, SaveProjectionsID, ord(SaveProjections));
SetDlogItem(MyLog, MinProjSizeID, ord(MinProjSize));
alldone := 0;
repeat {if we don't do it this way, ModalDialog throws us into code checking after each keystroke}
repeat {meaning you can't type in a 2 digit number}
ModalDialog(nil, item);
if item = SaveProjectionsID then begin
SaveProjections := not SaveProjections;
SetDlogItem(MyLog, SaveProjectionsID, ord(SaveProjections));
end;
if item = MinProjSizeID then begin
MinProjSize := not MinProjSize;
SetDlogItem(MyLog, MinProjSizeID, ord(MinProjSize));
end;
if (item = RotationAboutXID) or (item = RotationAboutYID) or (item = RotationAboutZID) then begin
case item of
RotationAboutXID:
AxisOfRotation := XAxis;
RotationAboutYID:
AxisOfRotation := YAxis;
RotationAboutZID:
AxisOfRotation := ZAxis;
end; {case}
SetDlogItem(MyLog, RotationAboutXID, ord(AxisOfRotation = XAxis));
SetDlogItem(MyLog, RotationAboutYID, ord(AxisOfRotation = YAxis));
SetDlogItem(MyLog, RotationAboutZID, ord(AxisOfRotation = ZAxis));
end;
if (item >= nearestID) and (item <= MeanID) then begin
case item of
NearestID:
ProjectionMethod := NearestPoint;
BrightestID:
ProjectionMethod := BrightestPoint;
MeanID:
ProjectionMethod := MeanValue;
end;
SetDlogItem(MyLog, NearestID, ord(projectionMethod = NearestPoint));
SetDlogItem(MyLog, BrightestID, ord(projectionMethod = BrightestPoint));
SetDlogItem(MyLog, MeanID, ord(projectionMethod = MeanValue));
end;
until (item = ok) or (item = cancel);
alldone := 1;
if (item = ok) then begin {check all the values that could have been entered, don't know which were changed}
Interval := GetDReal(MyLog, SliceIntervalID);
if (Interval <= 0.0) or (Interval > 1023.0) then begin
Interval := info^.StackInfo^.SliceSpacing;
SetDReal(MyLog, SliceIntervalID, Interval, 1);
beep;
alldone := 0;
end; {if Interval}
InitAngle := GetDNum(MyLog, InitAngleID);
if (InitAngle < 0) or (InitAngle > 360) then begin
InitAngle := SaveInitAngle;
SetDNum(MyLog, InitAngleID, InitAngle);
beep;
alldone := 0;
end; {if InitAngle}
TotalAngle := GetDNum(MyLog, TotalAngleID);
if (TotalAngle < 0) or (TotalAngle > 360) then begin
TotalAngle := SaveTotalAngle;
SetDNum(MyLog, TotalAngleID, TotalAngle);
beep;
alldone := 0;
end; {if TotalAngle}
AngleInc := GetDNum(MyLog, AngleIncID);
if (AngleInc < 0) or (AngleInc > 360) then begin
AngleInc := SaveAngleInc;
SetDNum(MyLog, AngleIncID, AngleInc);
beep;
alldone := 0;
end; {if AngleInc}
TransparencyLower := GetDNum(MyLog, TransparencyLowerID);
if (TransparencyLower < 0) or (TransparencyLower > 255) then begin
TransparencyLower := SaveLower;
SetDNum(MyLog, TransparencyLowerID, TransparencyLower);
beep;
alldone := 0;
end; {if TransparencyLower}
TransparencyUpper := GetDNum(MyLog, TransparencyUpperID);
if (TransparencyUpper < 0) or (TransparencyUpper > 255) then begin
TransparencyUpper := SaveUpper;
SetDNum(MyLog, TransparencyUpperID, TransparencyUpper);
beep;
alldone := 0;
end; {if TransparencyUpper}
Opacity := GetDNum(MyLog, OpacityID);
if (Opacity < 0) or (Opacity > 100) then begin
Opacity := SaveOpacity;
SetDNum(MyLog, OpacityID, Opacity);
beep;
alldone := 0;
end; {if Opacity}
PercentSurf := GetDNum(MyLog, DepthCueSurfID);
if (PercentSurf < 0) or (PercentSurf > 100) then begin
PercentSurf := 100 - DepthCueSurf;
SetDNum(MyLog, DepthCueSurfID, PercentSurf);
beep;
alldone := 0;
end; {if DepthCueSurf}
PercentInt := GetDNum(MyLog, DepthCueIntID);
if (PercentInt < 0) or (PercentInt > 100) then begin
PercentInt := 100 - DepthCueInt;
SetDNum(MyLog, DepthCueIntID, PercentInt);
beep;
alldone := 0;
end; {if DepthCueInt}
info^.StackInfo^.SliceSpacing := Interval;
end;
until (alldone = 1);
DisposeDialog(mylog);
if item = cancel then begin {if Cancel, keep the saved values}
InitAngle := SaveInitAngle;
TotalAngle := SaveTotalAngle;
AngleInc := SaveAngleInc;
Opacity := SaveOpacity;
AxisOfRotation := SaveAxisOfRotation;
SaveProjections := SaveSaveProjections;
MinProjSize := SaveMinProjSize;
TransparencyLower := SaveLower;
TransparencyUpper := SaveUpper;
ShowProjectDialogBox := false;
end
else begin
DepthCueSurf := 100 - PercentSurf;
DepthCueInt := 100 - PercentInt;
ShowProjectDialogBox := true;
end;
end;
procedure Project;
begin
if ShowProjectDialogBox then
DoProjection;
end;
end.